﻿//
// Copyright (c) 2011 David Díaz & Fran Pedreira
//
// This file is part of PlanSim.
//
// PlanSim is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PlanSim is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with PlanSim. If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace PlanSim.Core
{
    public class Proceso : IProceso
    {
		/// <summary>
		/// Identificador del proceso.
		/// </summary>
		public string Nombre
		{
			get { return nombre; }
		}
        string nombre;

		/// <summary>
		/// Cola de "preparados" a la que está vinculado actualmente el proceso.
		/// </summary>
		public ICola Cola
		{
			get { return cola; }
			set { cola = value; }
		}
		ICola cola;
	
		/// <summary>
		/// Contiene los estados del proceso en cada ciclo de simulación.
		/// </summary>
		public Historia Historia
		{
			get { return historia; }
		}
		Historia historia = new Historia();

		/// <summary>
		/// Obtiene el color con el que se representará el proceso en el cronograma.
		/// </summary>
		public Color Color 
		{ 
			get { return this.color; } 
		}
		public Color color = Color.Red;

		/// <summary>
		/// Obtiene el ciclo de simulación en el que el proceso entra en el planificador.
		/// </summary>
		public int Entrada
		{
			get { return entrada; }
			// set { entrada = value; }
		}
		private int entrada;

		/// <summary>
		/// Obtiene el ciclo de simulación en el que el proceso es asignado por primera vez a la CPU.
		/// </summary>
		public int? Inicio
		{
			get { return inicio; }
			//set { inicio = value; }
		}
		private int? inicio = null;

		/// <summary>
		/// Obtiene el ciclo de simulación en el que el proceso termina.
		/// </summary>
		public int? Fin
		{
			get { return fin; }
			set { fin = value; }
		}
		private int? fin = null;

		/// <summary>
		/// Duración total del proceso.
		/// </summary>
		public int Duracion
		{
			get { return duracion; }
			//set { duracion = value; }
		}
		private int duracion;

		/// <summary>
		/// Obtiene el número de ciclos de CPU pendientes de ejecutar por el proceso.
		/// </summary>
		public int Pendiente { get { return duracion - ciclosCPU; } }

		/// <summary>
		/// Obtiene el número de ciclos de CPU ya consumidos por el proceso.
		/// </summary>
		public int CiclosCPU
		{
			get { return ciclosCPU; }
			//set { ciclosAcumuladosCPU = value; }
		}
		int ciclosCPU;

		/// <summary>
		/// Obtiene o establece el número de espera de CPU que ha realizado el proceso.
		/// </summary>
		public int CiclosEsperaCPU
		{
			get { return ciclosEsperaCPU; }
			set { ciclosEsperaCPU = value; }
		}
		int ciclosEsperaCPU;

		/// <summary>
		/// Obtiene el número de ciclos de E/S ya consumidos por el proceso.
		/// </summary>
		public int CiclosES
		{
			get { return ciclosES; }
			//set { ciclosAcumuladosES = value; }
		}
		int ciclosES;
		
		/// <summary>
		/// Obtiene o establece el número de ciclos de espera de ES que ha realizado el proceso.
		/// </summary>
		public int CiclosEsperaES
		{
			get { return ciclosEsperaES; }
			set { ciclosEsperaES = value; }
		}
		int ciclosEsperaES;

		/// <summary>
		/// Número de ciclos de E/S que quedan por ejecutar para acabar la E/S en curso.
		/// </summary>
		int ciclosParcialesES;

		/// <summary>
		/// Indica cada cuantos ciclos de CPU el proceso hace una petición de E/S.
		/// </summary>
		int periodoES;

		/// <summary>
		/// Indica los ciclos de ES que hace el proceso antes de liberar el dispositivo.
		/// </summary>
		int ciclosRequeridosES;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="duracion">Duración de CPU del proceso.</param>
        public Proceso(string nombre, int entrada, int duracion, int periodoES, int ciclosES, Color color)
        {
            this.nombre = nombre;
			this.entrada = entrada;
            this.duracion = duracion;
			this.periodoES = periodoES;
			this.ciclosRequeridosES = ciclosES;
			this.color = color;

			this.ciclosCPU = 0;
			this.ciclosParcialesES = 0;
		}

		/// <summary>
		/// Ejecuta un ciclo de proceso.
		/// </summary>
        public void Ciclo(int ciclo)
        {
			// Si está en ES ejecuta un ciclo de E/S.
            if (!EntradaSalidaFinalizada())
                CicloES(ciclo);
			// Si no, si no ha finalizado ejecuta un ciclo de CPU.
			else if (!ProcesoFinalizado())
				CicloCPU(ciclo);
        }

		/// <summary>
		/// Lógica del proceso.
		/// </summary>
        private void CicloCPU(int ciclo)
        {
			if (!ProcesoFinalizado())
			{
				// Guardamos el ciclo de simulación en el que el proceso se asigna por primera vez a la CPU
				if (!this.inicio.HasValue)
					this.inicio = ciclo;

				// Actualizamos el último ciclo de simulación.
				this.fin = ciclo;

				++ciclosCPU;

				// Comprueba si tiene que hacer petición de ES
				if (!ProcesoFinalizado() && periodoES>0 && (ciclosCPU % periodoES == 0))
					ciclosParcialesES = ciclosRequeridosES;
			}
        }

		private void CicloES(int ciclo)
		{
			if (!EntradaSalidaFinalizada())
			{
				// Actualizamos el último ciclo de simulación.
				this.fin = ciclo;

				--ciclosParcialesES;
				++ciclosES;
			}
		}

        public bool ProcesoFinalizado()
        {
            // return (ciclosAcumuladosCPU == duracion);
			return (ciclosCPU==duracion && EntradaSalidaFinalizada());
        }

		public bool EntradaSalidaFinalizada()
		{
			return (ciclosParcialesES == 0);
		}
    }
}
